package model

import (
	myConfig "common/config"
	"common/helper"
	myRedis "common/redis"
	"common/userHelp"
	"context"
	"encoding/json"
	"fmt"
	"github.com/go-redis/redis/v8"
	"github.com/jmoiron/sqlx"
	"strconv"
	"strings"
	"time"
)

/*
更新日志到SQL
*/
var InsertLogCount = 100

var InsertTransactionCount = 1000

func UpdateLogToSql(redis *redis.Client, sqlCon *sqlx.DB) {
	if redis == nil || sqlCon == nil {
		return
	}

	for {
		for {
			valSql, popError := redis.RPop(context.Background(), myRedis.UpdateSqlField).Result()
			if popError != nil {
				break
			}
			_, err := sqlCon.Exec(valSql)
			if err != nil {
				//需要写日志
				helper.SqlErrLog("UpdateLogToSql update err:%s,\n ,%s", err.Error(), valSql)
			}
		}

		transactions, err := fetchTransactionsFromRedis(redis, myRedis.UpdateTransaction, InsertTransactionCount)
		if err != nil {
			helper.GetloggerInstance().Errorf("fetchTransactionsFromRedis:%s", err.Error())
		}

		if len(transactions) > 0 {
			// 插入数据到 MySQL
			if err := batchInsertWithTransaction(sqlCon, transactions); err != nil {
				insert := dialect.Insert("tbl_balance_transaction").Rows(transactions)
				sql, _, _ := insert.ToSQL()
				helper.SqlErrLog("tbl_balance_transaction\n %s", sql)
			}
		}

		for loopIndex := 0; loopIndex < InsertLogCount; loopIndex++ {
			valSql, popError := redis.RPop(context.Background(), myRedis.InsertLogKey).Result()
			if popError != nil {
				break
			}
			_, err := sqlCon.Exec(valSql)
			if err != nil {
				helper.SqlErrLog("UpdateLogToSql insert err:%s,\n ,%s", err.Error(), valSql)
			}
		}

		time.Sleep(100 * time.Millisecond) // 等待时间间隔后重试
	}
}

func fetchTransactionsFromRedis(rdb *redis.Client, key string, count int) ([]userHelp.MemberTransaction, error) {
	var transactions []userHelp.MemberTransaction

	for i := 0; i < count; i++ {
		data, err := rdb.RPop(ctx, key).Result()
		if err == redis.Nil {
			break // 队列为空
		}
		if err != nil {
			return transactions, err
		}

		var trans userHelp.MemberTransaction
		if err := json.Unmarshal([]byte(data), &trans); err != nil {
			return transactions, err
		}
		transactions = append(transactions, trans)
	}

	return transactions, nil
}

func batchInsertWithTransaction(db *sqlx.DB, transactions []userHelp.MemberTransaction) error {
	tx, err := db.Begin()
	if err != nil {
		return err
	}
	insert := dialect.Insert("tbl_balance_transaction").Rows(transactions)

	sql, args, err := insert.ToSQL()
	if err != nil {
		tx.Rollback()
		return err
	}

	if _, err := tx.Exec(sql, args...); err != nil {
		tx.Rollback()
		return err
	}

	if err := tx.Commit(); err != nil {
		return err
	}

	return nil
}

/*
更新用户数据到SQL
*/
var UpdateUserDateCount = 100
var AllMemTableName [13]string = [13]string{
	"tbl_member_adjust", "tbl_member_balance", "tbl_member_bankcard", "tbl_member_base", "tbl_member_info",
	"tbl_member_status", "tbl_member_controller_button", "tbl_member_accu", "tbl_proxy_base", "tbl_member_vip",
	"tbl_member_piggy_bank", "tbl_member_disbind_area", "tbl_member_lottery",
}

// var g_ThirdGameType [6]string = [6]string{"pp", "pg", "tada", "jdb", "evo", "jili"}
/*
var g_ThirdGameID [29]int64 = [29]int64{
	101,
	201,
	301,
	401,
	501, 502, 503,
	601, 602, 603,
	701, 702, 703,
	801, 802, 803,
	901, 902, 903,
	1001, 1002, 1003,
	1101, 1102,
}
*/
var g_StatisticsType [7]string = [7]string{"running", "gameTax", "gameWinLost", "gameRound", "validWaged", "sandbag_running", "sandbag_gameWinLost"}

func UpdateProxyStatisticsData(ctx context.Context, redis *redis.Client, sqlCon *sqlx.DB, valUid string) {
	redisKey := fmt.Sprintf("UserId:%s", valUid)
	timeStamp := time.Now().Unix()
	var updateKey string
	resGameType := myConfig.CopyGameType()
	for gameType, _ := range resGameType {
		updateKey = fmt.Sprintf("%d:%s", gameType, g_StatisticsType[0])
		running, _ := redis.HGet(ctx, redisKey, updateKey).Result()

		updateKey = fmt.Sprintf("%d:%s", gameType, g_StatisticsType[1])
		gameTax, _ := redis.HGet(ctx, redisKey, updateKey).Result()

		updateKey = fmt.Sprintf("%d:%s", gameType, g_StatisticsType[2])
		gameWinLost, _ := redis.HGet(ctx, redisKey, updateKey).Result()

		updateKey = fmt.Sprintf("%d:%s", gameType, g_StatisticsType[3])
		strGameRound, _ := redis.HGet(ctx, redisKey, updateKey).Result()

		updateKey = fmt.Sprintf("%d:%s", gameType, g_StatisticsType[4])
		strValidWaged, _ := redis.HGet(ctx, redisKey, updateKey).Result()

		var uid, nGameRound int64
		var flRunning, flGameWinLost, flGameTax, flValidWaged float64
		uid, _ = strconv.ParseInt(valUid, 10, 64)
		nGameRound, _ = strconv.ParseInt(strGameRound, 10, 64)
		flRunning, _ = strconv.ParseFloat(running, 64)
		flGameWinLost, _ = strconv.ParseFloat(gameWinLost, 64)
		flGameTax, _ = strconv.ParseFloat(gameTax, 64)
		flValidWaged, _ = strconv.ParseFloat(strValidWaged, 64)
		if flRunning > 0.00 || flGameTax > 0.00 || flGameWinLost != 0.00 || nGameRound > 0 || flValidWaged > 0.00 {
			_, errExec := sqlCon.Exec("CALL tbl_report_user_daily_update(?,?,?,?,?,?,?,?)", uid, gameType,
				flRunning, flGameWinLost, flGameTax, flValidWaged, nGameRound, timeStamp)
			if errExec == nil {
				//以增量累加的方式更新到数据库 设置成功后需要清除
				updateKey = fmt.Sprintf("%d:%s", gameType, g_StatisticsType[0])
				_, errG0 := redis.HSet(ctx, redisKey, updateKey, "0.00").Result()
				if errG0 != nil {
					helper.GetloggerInstance().Emergencyf("errG0:%s", errG0.Error())
				}

				updateKey = fmt.Sprintf("%d:%s", gameType, g_StatisticsType[1])
				_, errG1 := redis.HSet(ctx, redisKey, updateKey, "0.00").Result()
				if errG1 != nil {
					helper.GetloggerInstance().Emergencyf("errG1:%s", errG1.Error())
				}

				updateKey = fmt.Sprintf("%d:%s", gameType, g_StatisticsType[2])
				_, errG2 := redis.HSet(ctx, redisKey, updateKey, "0.00").Result()
				if errG2 != nil {
					helper.GetloggerInstance().Emergencyf("errG2:%s", errG2.Error())
				}

				updateKey = fmt.Sprintf("%d:%s", gameType, g_StatisticsType[3])
				_, errG3 := redis.HSet(ctx, redisKey, updateKey, "0").Result()
				if errG3 != nil {
					helper.GetloggerInstance().Emergencyf("errG3:%s", errG3.Error())
				}

				updateKey = fmt.Sprintf("%d:%s", gameType, g_StatisticsType[4])
				_, errG4 := redis.HSet(ctx, redisKey, updateKey, "0.00").Result()
				if errG4 != nil {
					helper.GetloggerInstance().Emergencyf("errG4:%s", errG4.Error())
				}
			} else {
				helper.GetloggerInstance().Emergencyf("CALL tbl_report_user_daily_update(%d,%d,%0.2f,%0.2f,%0.2f,%0.2f,%d,%d);	reason:%s ", uid, gameType,
					flRunning, flGameWinLost, flGameTax, flValidWaged, nGameRound, timeStamp, errExec.Error())
			}

			//处理新增的放水数据
			var flSandbagRunning, flSandbagGameWinLost float64
			updateKey = fmt.Sprintf("%d:%s", gameType, g_StatisticsType[5])
			SandbagRunning, _ := redis.HGet(ctx, redisKey, updateKey).Result()

			updateKey = fmt.Sprintf("%d:%s", gameType, g_StatisticsType[6])
			SandbagGameWinLost, _ := redis.HGet(ctx, redisKey, updateKey).Result()

			flSandbagRunning, _ = strconv.ParseFloat(SandbagRunning, 64)
			flSandbagGameWinLost, _ = strconv.ParseFloat(SandbagGameWinLost, 64)
			if flSandbagRunning > 0.00 || flSandbagGameWinLost > 0.00 {
				_, errExec := sqlCon.Exec("CALL tbl_report_game_daily_sandbag_update(?,?,?,?,?)", uid, gameType,
					flSandbagRunning, flSandbagGameWinLost, timeStamp)
				if errExec == nil {
					//以增量累加的方式更新到数据库 设置成功后需要清除
					updateKey = fmt.Sprintf("%d:%s", gameType, g_StatisticsType[5])
					_, errG0 := redis.HSet(ctx, redisKey, updateKey, "0.00").Result()
					if errG0 != nil {
						helper.GetloggerInstance().Emergencyf("errG0:%s", errG0.Error())
					}

					updateKey = fmt.Sprintf("%d:%s", gameType, g_StatisticsType[6])
					_, errG1 := redis.HSet(ctx, redisKey, updateKey, "0.00").Result()
					if errG1 != nil {
						helper.GetloggerInstance().Emergencyf("errG1:%s", errG1.Error())
					}
				} else {
					helper.GetloggerInstance().Emergencyf("CALL tbl_report_game_daily_sandbag_update(%d,%d,%0.2f,%0.2f,%d);	reason:%s ", uid, gameType,
						flRunning, flGameWinLost, timeStamp, errExec.Error())
				}
			}
		}
	}
}

func UpdateUserDataByUid(ctx context.Context, redis *redis.Client, sqlCon *sqlx.DB, valUid string) {
	redisKey := fmt.Sprintf("UserId:%s", valUid)
	for tableIndex := range AllMemTableName {
		tableName := AllMemTableName[tableIndex]
		updateField, err := redis.HGet(ctx, redisKey, tableName).Result()
		if err != nil {
			continue
		}

		tableFields := strings.Split(updateField, ":")
		if len(updateField) == 0 {
			continue
		}

		for arrIndex := 0; arrIndex < len(tableFields); arrIndex++ {
			updateKey := fmt.Sprintf("%s:%s", tableName, tableFields[arrIndex])
			updateValue, errGet := redis.HGet(ctx, redisKey, updateKey).Result()
			if errGet != nil {
				continue
			}

			//此处可优化 不需要每次都去查 做个新表旧表的判断就可以了
			var strQueryId string
			exceSqlQuery := fmt.Sprintf("SELECT uid FROM  %s where uid=%s", tableName, valUid)
			err := sqlCon.Get(&strQueryId, exceSqlQuery)
			if err == nil {
				exceSql := fmt.Sprintf("UPDATE %s set %s='%s' where uid='%s'", tableName, tableFields[arrIndex], updateValue, valUid)
				_, updateError := sqlCon.Exec(exceSql)
				if updateError != nil {
					helper.GetloggerInstance().Emergencyf("%s\n%s", exceSql, updateError.Error())
				}
			} else {
				exceSql := fmt.Sprintf("insert into  %s(uid,%s) values(%s,%s)", tableName, tableFields[arrIndex], valUid, updateValue)
				_, InsertError := sqlCon.Exec(exceSql)
				if InsertError != nil {
					helper.GetloggerInstance().Emergencyf("%s\n%s", exceSql, InsertError.Error())
				}
			}
		}
		redis.HSet(ctx, redisKey, tableName, "")
	}

}
func UpdateUserDataToSql(ctx context.Context, redis *redis.Client, sqlCon *sqlx.DB) {
	if redis == nil || sqlCon == nil {
		return
	}

	for {
		//先处理需要立即更新了用户数据
		for loopIndex := 0; loopIndex < UpdateUserDateCount; loopIndex++ {
			valUid, popError := redis.SPop(context.Background(), myRedis.ImmediateRoleSetKey).Result()
			if popError != nil {
				break
			}

			dataFieldLock := fmt.Sprintf("%s", valUid)
			myRedis.Lock(GetRedisInstance(), dataFieldLock)
			UpdateProxyStatisticsData(ctx, redis, sqlCon, valUid)
			UpdateUserDataByUid(ctx, redis, sqlCon, valUid)
			userHelp.SaveUserWageRequire(valUid)
			redis.SRem(context.Background(), "UpdateRoleSet", valUid)
			redisKey := fmt.Sprintf("UserId:%s", valUid)
			redis.HSet(ctx, redisKey, "lastUpdateTime", time.Now().Unix())
			myRedis.Unlock(GetRedisInstance(), dataFieldLock)
		}

		members, err := redis.SMembers(context.Background(), "UpdateRoleSet").Result()
		if err != nil {
			time.Sleep(50 * time.Millisecond)
			helper.SqlErrLog("UpdateUserDataToSql SMembers error:%s", err.Error())
			continue
		}

		timeLong := myConfig.GetGameCfgInt(myConfig.CfgUserUpdateDataTimeLong)
		if timeLong == 0 {
			timeLong = 5
		}

		// 输出集合中的所有成员
		for _, valUid := range members {
			redisKey := fmt.Sprintf("UserId:%s", valUid)

			//更新时间判断
			lastUpdateTime, _ := redis.HGet(ctx, redisKey, "lastUpdateTime").Result()
			nUpdateTime, _ := strconv.ParseInt(lastUpdateTime, 10, 64)
			if (time.Now().Unix() - nUpdateTime) <= int64(timeLong)*int64((time.Minute/time.Second)) {
				continue
			}

			dataFieldLock := fmt.Sprintf("%s", valUid)
			userHelp.GenInterest(valUid)
			myRedis.Lock(GetRedisInstance(), dataFieldLock)
			UpdateProxyStatisticsData(ctx, redis, sqlCon, valUid)
			UpdateUserDataByUid(ctx, redis, sqlCon, valUid)
			userHelp.SaveUserWageRequire(valUid)
			redis.SRem(context.Background(), "UpdateRoleSet", valUid)
			redis.HSet(ctx, redisKey, "lastUpdateTime", time.Now().Unix())
			myRedis.Unlock(GetRedisInstance(), dataFieldLock)
		}

		time.Sleep(50 * time.Millisecond) // 等待时间间隔后重试
	}
}
